// Copyright (c) 2019-present, iQIYI, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

// Created by caikelun on 2019-03-07.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include <errno.h>
#include <signal.h>
#include <sys/syscall.h>
#include <android/log.h>
#include "xcc_signal.h"
#include "xcc_errno.h"
#include "xcc_libc_support.h"

#define XCC_SIGNAL_CRASH_STACK_SIZE (1024 * 128)
//使用#pragma 声明是彰显 Objective-C 工艺的标志之一。虽然 #pragma 最初的目的是为了使得源代码在不同的编译器下兼容的，但精明的Xcode编码器将 #pragma 使用到了极致。
//在现在的背景下，#pragma 避开了注释和代码之间的界限。作为预处理指令，#pragma 在编译时进行计算。但它并不像如 #ifdef...#endif 之类的宏，#pragma 的使用方式不会改变你的应用运行时的行为。
// 相反的，#pragma 声明主要由 Xcode 用来完成两个主要任务：整理代码和防止编译器警告。
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpadded"
typedef struct {
  int signum;
  struct sigaction oldact;
} xcc_signal_crash_info_t;
#pragma clang diagnostic pop
//{.signum = SIGABRT},abort发出的信号
// {.signum = SIGBUS},非法内存访问
// {.signum = SIGFPE},浮点异常
// {.signum = SIGILL},非法指令
// {.signum = SIGSEGV},无效内存访问
// {.signum = SIGTRAP},断点或陷阱指令
// {.signum = SIGSYS},系统调用异常
// {.signum = SIGSTKFLT}栈溢出
static xcc_signal_crash_info_t xcc_signal_crash_info[] =
    {
        {.signum = SIGABRT},
        {.signum = SIGBUS},
        {.signum = SIGFPE},
        {.signum = SIGILL},
        {.signum = SIGSEGV},
        {.signum = SIGTRAP},
        {.signum = SIGSYS},
        {.signum = SIGSTKFLT}
    };
//int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);sigaction函数可以读取或者指定信号相关联的处理动作，signal与其功能类似
//     参数signum：指定信号的编号（利用kill -l命令可以查看）；

// 参数*act：若act指针非空，则根据act修改该信号的处理动作；

// 参数*oldact：若oldact指针非空，则通过oldact传出该信号原来的处理动作；

// 返回值：成功返回0，失败返回-1；
int xcc_signal_crash_register(void (*handler)(int, siginfo_t *, void *)) {
  stack_t ss;
  if (NULL == (ss.ss_sp = calloc(1, XCC_SIGNAL_CRASH_STACK_SIZE))) return XCC_ERRNO_NOMEM;
  ss.ss_size = XCC_SIGNAL_CRASH_STACK_SIZE;
  ss.ss_flags = 0;
  //sigalstack 是用于替换信号处理函数栈，SIGSEGV可能是栈溢出导致，sigaltstack为任意线程注册一个可选的栈，保留一下在紧急情况下使用的空间。
  if (0 != sigaltstack(&ss, NULL)) return XCC_ERRNO_SYS;

  struct sigaction act;
  memset(&act, 0, sizeof(act));
  sigfillset(&act.sa_mask);
  act.sa_sigaction = handler;
  act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
// // 注册信号处理器，并保存旧处理器，以便在处理器完毕后，重新将信号发送给旧处理器
// 通过 sigaction() 进行信号的安装,信号的处理函数在 xc_crash_signal_handler
  size_t i;
  for (i = 0; i < sizeof(xcc_signal_crash_info) / sizeof(xcc_signal_crash_info[0]); i++)
    if (0 != sigaction(xcc_signal_crash_info[i].signum, &act, &(xcc_signal_crash_info[i].oldact)))
      return XCC_ERRNO_SYS;

  return 0;
}

int xcc_signal_crash_unregister(void) {
  int r = 0;
  size_t i;
  for (i = 0; i < sizeof(xcc_signal_crash_info) / sizeof(xcc_signal_crash_info[0]); i++)
    if (0 != sigaction(xcc_signal_crash_info[i].signum, &(xcc_signal_crash_info[i].oldact), NULL))
      r = XCC_ERRNO_SYS;

  return r;
}

int xcc_signal_crash_ignore(void) {
  struct sigaction act;
  xcc_libc_support_memset(&act, 0, sizeof(act));
  sigemptyset(&act.sa_mask);
  act.sa_handler = SIG_DFL;
  act.sa_flags = SA_RESTART;

  int r = 0;
  size_t i;
  for (i = 0; i < sizeof(xcc_signal_crash_info) / sizeof(xcc_signal_crash_info[0]); i++)
    if (0 != sigaction(xcc_signal_crash_info[i].signum, &act, NULL))
      r = XCC_ERRNO_SYS;

  return r;
}
// 将信号重新发送出去，交给旧处理器处理
int xcc_signal_crash_queue(siginfo_t *si) {
  if (SIGABRT == si->si_signo || SI_FROMUSER(si)) {
    if (0 != syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), si->si_signo, si))
      return XCC_ERRNO_SYS;
  }

  return 0;
}

static sigset_t xcc_signal_trace_oldset;
static struct sigaction xcc_signal_trace_oldact;
//用来处理 SIG_QUIT 的响应函数是 xc_trace_handler() ，这个也是后面再来分析。函数的最后还会启动一个线程，并在线程响应函数xc_trace_dumper中等待 ANR 的发生。这里的等待机制同样是用的 eventfd。
int xcc_signal_trace_register(void (*handler)(int, siginfo_t *, void *)) {
  int r;
  sigset_t set;
  struct sigaction act;

  //un-block the SIGQUIT mask for current thread, hope this is the main thread
  sigemptyset(&set);
  sigaddset(&set, SIGQUIT);
  if (0 != (r = pthread_sigmask(SIG_UNBLOCK, &set, &xcc_signal_trace_oldset))) return r;

  //register new signal handler for SIGQUIT
  memset(&act, 0, sizeof(act));
  sigfillset(&act.sa_mask);
  act.sa_sigaction = handler;
  act.sa_flags = SA_RESTART | SA_SIGINFO;
  if (0 != sigaction(SIGQUIT, &act, &xcc_signal_trace_oldact)) {
    pthread_sigmask(SIG_SETMASK, &xcc_signal_trace_oldset, NULL);
    return XCC_ERRNO_SYS;
  }

  return 0;
}

void xcc_signal_trace_unregister(void) {
  pthread_sigmask(SIG_SETMASK, &xcc_signal_trace_oldset, NULL);
  sigaction(SIGQUIT, &xcc_signal_trace_oldact, NULL);
}
